library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.typeDefinitions.all;

entity idStage is
  port (
    clk            : in std_logic;
    nReset         : in std_logic;
    holdStage      : in std_logic;
    invalidate     : in std_logic;
    nPC_in         : in std_logic_vector(31 downto 0);
    pcOld_in       : in std_logic_vector(31 downto 0);
    instruction_in : in std_logic_vector(31 downto 0);

    writeDestination : in std_logic_vector(4 downto 0);
    writeData        : in std_logic_vector(31 downto 0);
    Wen              : in std_logic;

    forwardIn  : in idForwardIn;
    forwardOut : out idForwardOut;

    rs : out std_logic_vector(4 downto 0);
    rt : out std_logic_vector(4 downto 0);


    halt               : out std_logic;
    rdat1_out          : out std_logic_vector(31 downto 0);
    busA               : out std_logic_vector(31 downto 0);
    busB               : out std_logic_vector(31 downto 0);
    immediate          : out std_logic_vector(31 downto 0);
    upper              : out std_logic_vector(31 downto 0);
    pcOld              : out std_logic_vector(31 downto 0);
    jumpAddr           : out std_logic_vector(25 downto 0);
    destination        : out std_logic_vector(4 downto 0);
    shftamnt           : out std_logic_vector(4 downto 0);
    nPC_Sel            : out pcSel;
    instruction        : out operation;
    instruction_decode : out operation;

    nPC : out std_logic_vector(31 downto 0));
end idStage;

architecture idStage_arch of idStage is


  component immExt
    port (
      imm    : in  std_logic_vector(15 downto 0);
      sign   : in  std_logic;
      extend : out std_logic_vector(31 downto 0);
      upper  : out std_logic_vector(31 downto 0));
  end component;

  component registerFile
    port
      (
        wdat   : in  std_logic_vector (31 downto 0);
        wsel   : in  std_logic_vector (4 downto 0);
        wen    : in  std_logic;
        clk    : in  std_logic;
        nReset : in  std_logic;
        rsel1  : in  std_logic_vector (4 downto 0);
        rsel2  : in  std_logic_vector (4 downto 0);
        rdat1  : out std_logic_vector (31 downto 0);
        rdat2  : out std_logic_vector (31 downto 0)
        );
  end component;

  component idInterstageReg
    port (
      clk            : in std_logic;
      nReset         : in std_logic;
      holdStage      : in std_logic;
      invalidate     : in std_logic;
      nPC_in         : in std_logic_vector(31 downto 0);
      immediate_in   : in std_logic_vector(31 downto 0);
      upper_in       : in std_logic_vector(31 downto 0);
      busA_in        : in std_logic_vector(31 downto 0);
      busB_in        : in std_logic_vector(31 downto 0);
      rs_in          : in std_logic_vector(4 downto 0);
      rt_in          : in std_logic_vector(4 downto 0);
      shftamnt_in    : in std_logic_vector(4 downto 0);
      destination_in : in std_logic_vector(4 downto 0);
      instruction_in : in operation;

      immediate   : out std_logic_vector(31 downto 0);
      upper       : out std_logic_vector(31 downto 0);
      busA        : out std_logic_vector(31 downto 0);
      busB        : out std_logic_vector(31 downto 0);
      shftamnt    : out std_logic_vector(4 downto 0);
      rs          : out std_logic_vector(4 downto 0);
      rt          : out std_logic_vector(4 downto 0);
      destination : out std_logic_vector(4 downto 0);
      instruction : out operation;
      nPC         : out std_logic_vector(31 downto 0));
  end component;

  component instDecode
    port (
      fnct   : in  std_logic_vector(5 downto 0);
      opcode : in  std_logic_vector(5 downto 0);
      op     : out operation);
  end component;

  signal t_rs, regDest, rd, t_rt, t_shftamnt : std_logic_vector(4 downto 0);
  signal opcode                              : std_logic_vector(5 downto 0);
  signal fnct                                : std_logic_vector(5 downto 0);
  signal t_imm16                             : std_logic_vector(15 downto 0);
  signal rdat1, rdat2, t_rdat1, t_rdat2      : std_logic_vector(31 downto 0);
  signal t_upper, extend                     : std_logic_vector(31 downto 0);
  signal op                                  : operation;
  signal equal, extImm                       : std_logic;
  signal tnPC_Sel : pcSel;
  
begin  -- fetchInterstageReg
  --

  instruction_decode <= op;
  rdat1_out          <= rdat1;

  pcOld <= pcOld_in;

  equal <= '1' when (rdat1 = rdat2) else
           '0';

  nPC_Sel <= tnPC_sel;
  forwardOut.rs <= t_rs;
  forwardOut.rt <= t_rt;
  forwardOut.nPCSel <= tnPC_Sel;

  t_rs       <= instruction_in(25 downto 21);
  t_rt       <= instruction_in(20 downto 16);
  rd         <= instruction_in(15 downto 11);
  t_imm16    <= instruction_in(15 downto 0);
  opcode     <= instruction_in(31 downto 26);
  fnct       <= instruction_in(5 downto 0);
  t_shftamnt <= instruction_in(10 downto 6);
  jumpAddr   <= instruction_in(25 downto 0);

  reg_c : registerFile port map (
    wdat   => writeData,
    wsel   => writeDestination,
    wen    => Wen,
    clk    => clk,
    nReset => nReset,
    rsel1  => t_rs,
    rsel2  => t_rt,
    rdat1  => t_rdat1,
    rdat2  => t_rdat2);

  decode_c : instDecode port map (
    fnct   => fnct,
    opcode => opcode,
    op     => op);

  inter_c : idInterstageReg port map (
    clk            => clk,
    nReset         => nReset,
    holdStage      => holdStage,
    invalidate     => invalidate,
    nPC_in         => nPC_in,
    immediate_in   => extend,
    upper_in       => t_upper,
    rs_in          => t_rs,
    rt_in          => t_rt,
    busA_in        => rdat1,
    busB_in        => rdat2,
    shftamnt_in    => t_shftamnt,
    destination_in => regDest,
    instruction_in => op,
    immediate      => immediate,
    upper          => upper,
    rs             => rs,
    rt             => rt,
    busA           => busA,
    busB           => busB,
    shftamnt       => shftamnt,
    instruction    => instruction,
    destination    => destination,
    nPC            => nPC);

  ext_c : immExt port map(
    imm    => t_imm16,
    sign   => extImm,
    extend => extend,
    upper  => t_upper
    );

  forwA : process (t_rdat1, forwardIn)
  begin  -- process t_rdat1
    case forwardIn.selA is
      when '1' =>
        rdat1 <= forwardIn.busA;
      when others =>
        rdat1 <= t_rdat1;
    end case;
  end process forwA;

  forwB : process (t_rdat2, forwardIn)
  begin  -- process t_rdat1
    case forwardIn.selB is
      when '1' =>
        rdat2 <= forwardIn.busB;
      when others =>
        rdat2 <= t_rdat2;
    end case;
  end process forwB;


  pc : process (op, equal)
  begin  -- process pc
    case op is
      when o_jr =>
        tnPC_Sel <= pc_busA;
      when o_j | o_jal =>
        tnPC_Sel <= pc_adj;
      when o_beq =>
        if(equal = '1') then
          tnPC_Sel <= pc_ext;
        else
          tnPC_Sel <= pc_add4;
        end if;
      when o_bne =>
        if(equal = '0') then
          tnPC_Sel <= pc_ext;
        else
          tnPC_Sel <= pc_add4;
        end if;
      when others =>
        tnPC_Sel <= pc_add4;
    end case;
  end process pc;

  extension : process (op)
  begin  -- process extImm ... 1 = sign extend
    case op is
      when o_xori | o_ori | o_andi =>   --31
        extImm <= '0';
      when others =>                    --rt
        extImm <= '1';
    end case;
  end process extension;

  halt_P : process (op)
  begin  -- process extImm ... 1 = sign extend
    case op is
      when o_halt =>                    --31
        halt <= '1';
      when others =>                    --rt
        halt <= '0';
    end case;
  end process halt_P;

  destination_p : process (op, rd, t_rt)
  begin  -- process destination
    case op is
      when o_jal =>                                          --31
        regDest <= std_logic_vector(to_unsigned(31, 5));
      when o_addu | o_and | o_nor | o_or | o_slt | o_sltu |  --rd
        o_sll | o_srl | o_subu | o_xor =>
        regDest <= rd;
      when o_bne| o_beq| o_j| o_jr| o_sw =>
        regDest <= (others => '0');
      when others =>                                         --rt
        regDest <= t_rt;
    end case;
  end process destination_p;
  
end idStage_arch;
